home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
050
/
madtrb13.arc
/
RVERSI.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1985-05-26
|
31KB
|
1,126 lines
{$C-}
{$R-}
{$U-}
{$K-}
program reversi;
{
Program REVERSI by M. Quinlan 5/26/84
based on a program in the book Advanced Pascal Programming Techniques
by Paul A. Sand
Version 1 Release 0 Modification level 0
Program from the book modified for the IBM PC and Turbo Pascal.
Version 1 Release 1 Modification level 0
Use all-points-addressable graphics for the board display (loosly based on
the section in the book titled "Modifying reversi for Graphics").
Version 1 Release 2 Modification level 0
Use customized characters in medium-resolution graphics mode for the
board display (much faster than APA graphics). On the "human" move,
show him which discs would be flipped.
Version 1 Release 3 Modification level 0
Allow human vs. human and computer vs. computer.
Version 1 Release 4 Modification level 0
Display version on the screen.
Change board evaluation routine:
if end of game detected, base evaluation totally on the score
make the sides more important
make "poison2" squares less important
* Version 1 Release 4 Modification level 1
Make minor changes so the input will work correctly with Turbo Pascal
Version 3.01a and PC DOS 3.1.
==============================================================================
Possible future enhancements:
Help system on entry to describe rules of game, possible strategy, and
this implementation (how to make a move, etc.).
Allow human to ask computer for a suggested move.
Allow setup mode where human can put disc of either color an any square.
Allow change of mode or player at any time (i.e. play for a while then let
the computer take over, etc.).
Better handle arrow keys when human selects move: allow Up, Down, Left, Right
and have the keys move to the next legal square in that direction.
Improve performance.
Improve the level of play.
}
const
MAXMOVES = 60;
LIGHT = 0;
DARK = 1;
EMPTY = 2;
BORDER = 3;
type
contents = LIGHT..BORDER;
plcolor = LIGHT..DARK;
pltype = (COMPUTERPLAYER, HUMANPLAYER);
squarenum = 0..99;
movelist = record
nmoves: 0..MAXMOVES;
move: array [1..MAXMOVES] of squarenum
end;
board = record
sq: array [squarenum] of contents;
ndiscs: array [plcolor] of integer;
possible: movelist
end;
direction = (NORTH, NORTHEAST, EAST, SOUTHEAST,
SOUTH, SOUTHWEST, WEST, NORTHWEST);
var
ch: char;
delta: array [direction] of integer;
sqord: array [squarenum] of integer;
sqchar: array [contents] of char;
corner, poison1, good1: array [1..4] of squarenum;
poison2, good2: array [1..4, 1..2] of squarenum;
edge: array [1..4, 1..4] of squarenum;
{ GRAPHDRA.PAS }
const
xbase = 0;
ybase = 4;
xscale = 2;
yscale = 2;
PIXELSPERCHAR = 8;
bgcolor = 0;
palcolor = 3;
black = 0;
lcyan = 1;
lmagenta = 2;
white = 3;
SQLIGHT = 0;
SQDARK = 1;
SQEMPTY = 2;
SQCURSOR = 3;
SQTOBELIGHT = 4;
SQTOBEDARK = 5;
type
color = black..white;
sqtype = SQLIGHT..SQTOBEDARK;
type
chardefarray = array[0..191] of byte;
const
chardef: chardefarray = ($FF, $80, $80, $9F, $9F, $9F, $9F, $9F,
$FF, $01, $01, $F9, $F9, $F9, $F9, $F9,
$9F, $9F, $9F, $9F, $9F, $80, $80, $FF,
$F9, $F9, $F9, $F9, $F9, $01, $01, $FF,
$FF, $80, $80, $9F, $9F, $98, $98, $98,
$FF, $01, $01, $F9, $F9, $19, $19, $19,
$98, $98, $98, $9F, $9F, $80, $80, $FF,
$19, $19, $19, $F9, $F9, $01, $01, $FF,
$FF, $80, $80, $80, $80, $80, $80, $80,
$FF, $01, $01, $01, $01, $01, $01, $01,
$80, $80, $80, $80, $80, $80, $80, $FF,
$01, $01, $01, $01, $01, $01, $01, $FF,
$FF, $80, $80, $80, $80, $80, $80, $80,
$FF, $01, $01, $01, $01, $01, $01, $01,
$80, $80, $80, $80, $80, $80, $80, $FF,
$01, $01, $01, $01, $01, $01, $01, $FF,
$FF, $80, $80, $9F, $9F, $98, $98, $98,
$FF, $01, $01, $F9, $F9, $19, $19, $19,
$98, $98, $98, $9F, $9F, $80, $80, $FF,
$19, $19, $19, $F9, $F9, $01, $01, $FF,
$FF, $80, $80, $9F, $9F, $9F, $9F, $9F,
$FF, $01, $01, $F9, $F9, $F9, $F9, $F9,
$9F, $9F, $9F, $9F, $9F, $80, $80, $FF,
$F9, $F9, $F9, $F9, $F9, $01, $01, $FF);
procedure initgraph;
begin
GraphColorMode;
GraphBackground(bgcolor);
Palette(palcolor);
Textcolor(lcyan);
MemW[$0000:$007E] := Seg(chardef);
MemW[$0000:$007C] := Ofs(chardef);
end;
procedure dispgrid;
begin { dispgrid }
end; { dispgrid }
procedure buildsquare;
begin
end;
procedure fillbkgrnd(i, j: integer);
var
x, y: integer;
xpscale, ypscale: integer;
begin
xpscale := PIXELSPERCHAR * xscale;
ypscale := PIXELSPERCHAR * yscale;
x := ((i * xscale) + xbase) * PIXELSPERCHAR;
y := ((j * yscale) + ybase) * PIXELSPERCHAR;
draw(x + 1, y + 1, x + xpscale - 1, y + 1, lmagenta);
draw(x + 1, y + 2, x + xpscale - 1, y + 2, lmagenta);
draw(x + 1, y + ypscale - 2, x + xpscale - 1, y + ypscale - 2, lmagenta);
draw(x + 1, y + ypscale - 3, x + xpscale - 1, y + ypscale - 3, lmagenta);
draw(x + 1, y + 3, x + 1, y + ypscale - 3, lmagenta);
draw(x + 2, y + 3, x + 2, y + ypscale - 3, lmagenta);
draw(x + xpscale - 2, y + 3, x + xpscale - 2, y + ypscale - 3, lmagenta);
draw(x + xpscale - 3, y + 3, x + xpscale - 3, y + ypscale - 3, lmagenta)
end;
procedure fillsquare(i,j: integer; c: color);
var
x, y, xpscale, ypscale, k: integer;
begin
xpscale := PIXELSPERCHAR * xscale;
ypscale := PIXELSPERCHAR * yscale;
x := ((i * xscale) + xbase) * PIXELSPERCHAR;
y := ((j * yscale) + ybase) * PIXELSPERCHAR;
for k := 1 to (ypscale - 2) do
draw(x + 1, y + k, x + xpscale - 1, y + k, c)
end;
procedure fillcursor(i, j: integer);
begin
fillsquare(i, j, lmagenta)
end;
procedure clearsquare(i, j: integer);
begin
fillsquare(i, j, black)
end;
procedure drawsquare(k: squarenum; c: sqtype);
var
i, j, ch: integer;
begin
i := k mod 10 - 1;
j := k div 10 - 1;
Textcolor(white);
GotoXY(i*xscale + xbase + 1, j*yscale + ybase + 1);
ch := (c*4) + $80;
write(char(ch));
write(char(ch+1));
GotoXY(i*xscale + xbase + 1, j*yscale + ybase + 2);
write(char(ch+2));
write(char(ch+3));
if (c = SQTOBEDARK) or (c = SQTOBELIGHT) then
fillbkgrnd(i, j)
else if c = SQCURSOR then
fillcursor(i, j);
TextColor(lcyan)
end;
{ CRTSTUFF.PAS }
type
crtcommand = (HOME, CLEAR, UP, DOWN, LEFT, RIGHT, BEEP);
g_string = string[255];
charset = set of char;
procedure crt(cc: crtcommand);
var
i: integer;
begin
case cc of
HOME:
GotoXY(1,1);
CLEAR:
initgraph;
UP:
if WhereY > 1 then
GotoXY(WhereX, WhereY - 1);
DOWN:
if WhereY < 24 then
GotoXY(WhereX, WhereY + 1);
LEFT:
if WhereX > 1 then
GotoXY(WhereX - 1, WhereY);
RIGHT:
if WhereX < 40 then
GotoXY(WhereX + 1, WhereY);
BEEP:
for i:=1 to 2 do begin
Sound(220);
Delay(100);
NoSound;
Delay(50)
end
end
end;
procedure eraseline(row: integer);
begin
GotoXY(1, row);
write(' ':40);
GotoXY(1, row)
end;
procedure center(s: g_string; row: integer);
begin
eraseline(row);
GotoXY( (40 - length(s) + 1) div 2, row);
write(s)
end;
procedure disptitle(s: g_string);
var
i, nch: integer;
begin
center(s, 1);
end;
function getkey(var ch: char; valid: charset; shiftlock: boolean): char;
var
ok: boolean;
begin
repeat
readln(ch);
if shiftlock then
ch := UpCase(ch);
ok := ch in valid;
if not ok then
crt(BEEP)
until ok;
getkey := ch
end;
{ INITREV.PAS }
procedure initrev;
var
i, j, sv: integer;
begin { initrev }
sqchar[DARK] := 'B';
sqchar[LIGHT] := 'W';
sqchar[EMPTY] := ' ';
sqchar[BORDER] := '*';
sqord[11] := 1; sqord[12] := 7; sqord[13] := 2; sqord[14] := 2;
sqord[22] := 8; sqord[23] := 6; sqord[24] := 5;
sqord[33] := 3; sqord[34] := 4;
sqord[44] := 0;
for j := 1 to 4 do
for i := j to 4 do
begin
sv := sqord[10 * j + i];
sqord[10 * i + j] := sv;
sqord[10 * (9 - i) + j] := sv;
sqord[10 * (9 - j) + i] := sv;
sqord[10 * j + 9 - i] := sv;
sqord[10 * i + 9 - j] := sv;
sqord[10 * (9 - i) + 9 - j] := sv;
sqord[10 * (9 - j) + 9 - i] := sv
end;
delta[NORTH] := -10;
delta[NORTHEAST] := -9;
delta[EAST] := 1;
delta[SOUTHEAST] := 11;
delta[SOUTH] := 10;
delta[SOUTHWEST] := 9;
delta[WEST] := -1;
delta[NORTHWEST] := -11;
corner[1] := 11; poison2[1, 1] := 12; good2[1, 1] := 13;
poison2[1, 2] := 21; poison1[1] := 22;
good2[1, 2] := 31; good1[1] := 33;
corner[2] := 18; poison2[2, 1] := 17; good2[2, 1] := 16;
poison2[2, 2] := 28; poison1[2] := 27;
good2[2, 2] := 38; good1[2] := 36;
corner[3] := 81; poison2[3, 1] := 82; good2[3, 1] := 83;
poison2[3, 2] := 71; poison1[3] := 72;
good2[3, 2] := 61; good1[3] := 63;
corner[4] := 88; poison2[4, 1] := 87; good2[4, 1] := 86;
poison2[4, 2] := 78; poison1[4] := 77;
good2[4, 2] := 68; good1[4] := 66;
for i := 1 to 4 do
begin
edge[1, i] := 12 + i;
edge[2, i] := 28 + 10 * i;
edge[3, i] := 21 + 10 * i;
edge[4, i] := 82 + i
end
end; { initrev }
{ DISPSQUA.PAS }
procedure dispsquare(k: squarenum; c: contents);
begin { dispsquare }
case c of
LIGHT: drawsquare(k, sqlight);
DARK : drawsquare(k, sqdark);
EMPTY: drawsquare(k, sqempty);
BORDER: drawsquare(k, sqcursor)
end
end; { dispsquare }
{ ITOS.PAS }
procedure itos(n, wid: integer; var s: g_string);
var
negnum: boolean;
begin { debugproc('itos'); }
negnum := (n < 0);
n := abs(n);
s := '';
repeat
s := chr(n mod 10 + 48) + s;
n := n div 10
until n = 0;
if negnum then
s := '-' + s;
while length(s) < wid do
s := ' ' + s;
end; { itos }
{ FLANKING.PAS }
function flanking(k: squarenum; dir: direction; var bd: board; pl: plcolor):
boolean;
var
ok: boolean;
opponent: plcolor;
del: integer;
begin { flanking }
ok := FALSE;
opponent := 1-pl;
del := delta[dir];
k := k + del;
with bd do
if sq[k] = opponent then
begin
repeat
k := k + del
until sq[k] <> opponent;
ok := (sq[k] = pl)
end;
flanking := ok
end; { flanking }
{ LEGALMOV.PAS }
function legalmove(k: squarenum; var bd: board; pl: plcolor): boolean;
var
ok: boolean;
dir: direction;
begin { legalmove }
dir := NORTH;
ok := flanking(k, dir, bd, pl);
while (dir <> NORTHWEST) and not ok do
begin
dir := succ(dir);
ok := flanking(k, dir, bd, pl)
end;
legalmove := ok
end; { legalmove}
{ MAKELIST.PAS }
function makelist(var legal: movelist; pl: plcolor; var bd: board): integer;
var
i: integer;
begin { makelist }
legal.nmoves := 0;
with bd.possible do
for i := 1 to nmoves do
if legalmove(move[i], bd, pl) then
begin
legal.nmoves := legal.nmoves + 1;
legal.move[legal.nmoves] := move[i]
end;
makelist := legal.nmoves
end; { makelist }
{ DELMOVE.PAS }
procedure delmove(k: squarenum; var list: movelist);
var
i: integer;
begin { debugproc('delmove'); }
with list do
begin
move[nmoves + 1] := k;
i := 1;
while move[i] <> k do i := i + 1;
if i < nmoves + 1 then
begin
while i <= nmoves - 1 do
begin
move [i] := move[i + 1];
i := i + 1
end;
nmoves := nmoves - 1
end
end
end; { delmove }
{ ADDMOVE.PAS }
procedure addmove(k: squarenum; var list: movelist);
var
i: integer;
begin { debugproc('addmove'); }
with list do
begin
move[nmoves + 1] := k;
i := 1;
while move[i] <> k do
i := i + 1;
if i = nmoves + 1 then
nmoves := nmoves + 1
end
end; { addmove }
procedure playgame;
var
mainboard: board;
list: movelist;
gameover, moved: boolean;
currentplayer: plcolor;
playertype: array [plcolor] of pltype;
lookahead: integer;
k: squarenum;
{ SETSQUAR.PAS }
procedure setsquare(k: squarenum; c: contents);
begin { debugproc('setsquare'); }
mainboard.sq[k] := c;
dispsquare(k, c)
end; { setsquare }
{ DISPSCOR.PAS }
procedure dispscore;
var
s: string[255];
begin { dispscore }
with mainboard do
begin
itos(ndiscs[LIGHT], 2, s);
GotoXY(37,6);
write(s);
itos(ndiscs[DARK], 2, s);
GotoXY(37,7);
write(s)
end
end; { dispscore }
{ INITGAME.PAS }
procedure initgame;
var
i, j: integer;
ch: char;
begin { initgame }
with mainboard do
begin
for i := 0 to 9 do
begin
sq[i] := BORDER;
sq[i + 90] := BORDER;
sq[10 * i] := BORDER;
sq[10 * i + 9] := BORDER
end;
ndiscs[LIGHT] := 2;
ndiscs[DARK] := 2;
with possible do
begin
nmoves := 12;
move[ 1] := 33;
move[ 2] := 34;
move[ 3] := 35;
move[ 4] := 36;
move[ 5] := 43;
move[ 6] := 46;
move[ 7] := 53;
move[ 8] := 56;
move[ 9] := 63;
move[10] := 64;
move[11] := 65;
move[12] := 66
end
end;
for i := 1 to 8 do
for j := 1 to 8 do
setsquare(10 * i + j, EMPTY);
setsquare(44, LIGHT);
setsquare(55, LIGHT);
setsquare(45, DARK);
setsquare(54, DARK);
for i := 5 to 9 do begin
GotoXY(21, i);
write(' ':20)
end;
eraseline(23);
eraseline(24);
GotoXY(1,23);
write('Player types: C = computer, H = Human');
GotoXY(1,24);
write('White player (C/H): ');
case getkey(ch, ['C', 'H'], TRUE) of
'C': playertype[LIGHT] := COMPUTERPLAYER;
'H': playertype[LIGHT] := HUMANPLAYER;
end;
eraseline(24);
GotoXY(1,24);
write('Black player (C/H): ');
case getkey(ch, ['C', 'H'], TRUE) of
'C': playertype[DARK] := COMPUTERPLAYER;
'H': playertype[DARK] := HUMANPLAYER;
end;
eraseline(23);
eraseline(24);
if (playertype[LIGHT] = COMPUTERPLAYER) or (playertype[DARK] = COMPUTERPLAYER) then
begin
GotoXY(1,24);
write('Enter lookahead for computer (1-6): ');
lookahead := ord(getkey(ch, ['1'..'6'], FALSE)) - 48;
GotoXY(28,8);
write('Lookahead:');
write(ch)
end;
eraseline(24);
GotoXY(31,6);
write('White:');
GotoXY(31,7);
write('Black:');
end; { initgame }
function getcomputer(var list: movelist): squarenum;
var
max: integer;
best: squarenum;
{ EVAL.PAS }
function eval(var bd: board; pl: plcolor; ourpl: plcolor): integer;
const
K1 = 1; { weighting factor for disc advantage }
K2 = 3; { weighting factor for mobility }
K3 = 200; { score for owning corner }
K4 = -100; { penalty for owning poison1 square }
K5 = 50; { score for owning good1 square }
K6 = -15; { penalty for owning poison2 square }
K7 = 15; { score for owning good2 square }
K8 = 20; { score for having ownly discs on edge }
K9 = 30; { score for occupying edge }
var
list: movelist;
i, j, score: integer;
c: contents;
sideset: set of contents;
opp: plcolor;
plmoves: integer;
function endgame: boolean;
begin
endgame := FALSE;
if plmoves = 0 then
begin
if makelist(list, 1-pl, bd) = 0 then
endgame := TRUE
end
end;
begin { eval }
opp := 1 - ourpl;
with bd do begin
score := K1 * (ndiscs[ourpl] - ndiscs[opp]);
plmoves := makelist(list, pl, bd);
if endgame then
begin
if score > 0 then
eval := maxint
else if score < 0 then
eval := -maxint
else
eval := 0
end
else begin
if pl = ourpl then
score := score + k2 * plmoves
else
score := score - K2 * plmoves;
for i := 1 to 4 do begin
c := sq[corner[i]];
if c = ourpl then
score := score + K3
else if c = opp then
score := score - K3
else begin { corner empty, check poison squares }
c := sq[poison1[i]];
if c = ourpl then
score := score + K4
else if c = opp then
score := score - K4
else begin
c := sq[good1[i]];
if c = ourpl then
score := score + K5
else if c = opp then
score := score - K5
end;
for j := 1 to 2 do begin
c := sq[poison2[i, j]];
if c = ourpl then
score := score + K6
else if c = opp then
score := score - K6
else begin
c := sq[good2[i, j]];
if c = ourpl then
score := score + k7
else if c = opp then
score := score - K7
end
end
end
end;
for i := 1 to 4 do begin
sideset := [];
for j := 1 to 4 do
sideset := sideset + [sq[edge[i, j]]];
if sideset = [ourpl] then
score := score + K9
else if sideset = [ourpl, EMPTY] then
score := score + K8
else if sideset = [opp, EMPTY] then
score := score - K8
else if sideset = [opp] then
score := score - K9
end;
eval := score
end
end
end; { eval }
{ TRYMOVE.PAS }
procedure trymove(trysq: squarenum; pl: plcolor; var bd: board);
var
dir: direction;
k1: squarenum;
opp: plcolor;
del: integer;
begin { trymove }
opp := 1 - pl;
with bd do begin
sq[trysq] := pl;
ndiscs[pl] := ndiscs[pl] + 1;
delmove(trysq, possible);
for dir := NORTH to NORTHWEST do begin
del := delta[dir];
if flanking(trysq, dir, bd, pl) then begin
k1 := trysq + del;
repeat
sq[k1] := pl;
ndiscs[pl] := ndiscs[pl] + 1;
ndiscs[opp] := ndiscs[opp] - 1;
k1 := k1 + del
until sq[k1] = pl
end
else if sq[trysq + del] = EMPTY then
addmove(trysq + del, possible)
end
end
end; { trymove }
{ SORTLIST.PAS }
procedure sortlist(var list: movelist);
var
i, j, jg, gap, k: integer;
begin { sortlist }
with list do begin
gap := nmoves div 2;
while gap > 0 do begin
for i := gap + 1 to nmoves do begin
j := i - gap;
while j > 0 do begin
jg := j + gap;
if sqord[move[j]] <= sqord[move[jg]] then
j := 0
else begin
k := move[j];
move[j] := move[jg];
move[jg] := k
end;
j := j - gap
end
end;
gap := gap div 2
end
end
end; { sortlist }
{ FINDMAX.PAS }
function findmin(look: integer; var list: movelist; var bd: board;
cutoff: integer; var bestmove: squarenum; ourpl: plcolor): integer;
forward;
function findmax(look: integer; var list: movelist; var bd: board;
cutoff: integer; var bestmove: squarenum; ourpl: plcolor): integer;
var
newlist: movelist;
newbd: board;
i, maxscore, score, nm: integer;
junk: squarenum;
opp: plcolor;
begin { findmax }
opp := 1 - ourpl;
sortlist(list);
with list do
if nmoves > 0 then begin
maxscore := -MAXINT;
i := 1;
repeat
newbd := bd;
trymove(move[i], ourpl, newbd);
if look <= 1 then
score := eval(newbd, opp, ourpl)
else begin
nm := makelist(newlist, opp, newbd);
score := findmin(look - 1, newlist, newbd, maxscore, junk, ourpl)
end;
if score > maxscore then begin
maxscore := score;
bestmove := move[i]
end;
i := i + 1
until (i > nmoves) or (maxscore >= cutoff)
end
else begin { no legal move }
if look <= 1 then
maxscore := eval(bd, opp, ourpl)
else begin
nm := makelist(newlist, opp, bd);
maxscore := findmin(look - 1, newlist, bd, -MAXINT, junk, ourpl)
end
end;
findmax := maxscore
end; { findmax }
{ FINDMIN.PAS }
function findmin;
var
newlist: movelist;
newbd: board;
i, minscore, score, nm: integer;
junk: squarenum;
opp: plcolor;
begin { findmin }
opp := 1 - ourpl;
sortlist(list);
with list do
if nmoves > 0 then begin
minscore := MAXINT;
i := 1;
repeat
newbd := bd;
trymove(move[i], opp, newbd);
if look <= 1 then
score := eval(newbd, ourpl, ourpl)
else begin
nm := makelist(newlist, ourpl, newbd);
score := findmax(look - 1, newlist, newbd, minscore, junk, ourpl)
end;
if score < minscore then begin
minscore := score;
bestmove := move[i]
end;
i := i + 1
until (i > nmoves) or (minscore <= cutoff)
end
else begin { no legal move }
if look <= 1 then
minscore := eval(bd, ourpl, ourpl)
else begin
nm := makelist(newlist, ourpl, bd);
minscore := findmax(look - 1, newlist, bd, MAXINT, junk, ourpl)
end
end;
findmin := minscore
end; { findmin }
begin { getcomputer }
if list.nmoves = 1 then { only 1 legal move }
getcomputer := list.move[1]
else begin
max := findmax(lookahead, list, mainboard, MAXINT, best, currentplayer);
getcomputer := best
end
end; { getcomputer }
{ GETHUMAN.PAS }
procedure makeflip(var fl: movelist; cp: plcolor; mv: squarenum; var bd: board);
var
dir: direction;
k1: squarenum;
del: integer;
i: integer;
begin
fl.nmoves := 0;
bd.sq[mv] := cp;
for dir := NORTH to NORTHWEST do
begin
del := delta[dir];
if flanking(mv, dir, bd, cp) then
begin
k1 := mv + del;
repeat
fl.nmoves := fl.nmoves + 1;
fl.move[fl.nmoves] := k1;
k1 := k1 + del
until bd.sq[k1] = cp
end
end;
bd.sq[mv] := EMPTY
end;
function gethuman(var list: movelist): squarenum;
type
movekey = (ACCEPT, NEXTMOVE, PREVMOVE);
var
i, j: integer;
ch: char;
m: movekey;
fliplist: movelist;
sq: integer;
function getmovekey: movekey;
var
ch: char;
gotkey: boolean;
begin { getmovekey }
gotkey := FALSE;
while not gotkey do
begin
read(kbd, ch);
if ch = char(27) then
begin
read(kbd, ch);
if ch = 'K' then { left arrow }
begin
gotkey := TRUE;
getmovekey := PREVMOVE
end
else if ch = 'M' then { right arrow }
begin
gotkey := TRUE;
getmovekey := NEXTMOVE
end
else
crt(BEEP);
end
else { ch <> char(27) }
if ch = char(13) then
begin
gotkey := TRUE;
getmovekey := ACCEPT
end
else
crt(BEEP);
end { while not gotkey }
end; { getmovekey }
begin { gethuman }
i := 1;
crt(BEEP);
with list do
begin
repeat
dispsquare(move[i], BORDER);
if currentplayer = dark then
sq := SQTOBEDARK
else
sq := SQTOBELIGHT;
makeflip(fliplist, currentplayer, move[i], mainboard);
for j := 1 to fliplist.nmoves do
drawsquare(fliplist.move[j], sq);
m := getmovekey;
for j := fliplist.nmoves downto 1 do
drawsquare(fliplist.move[j], 1-currentplayer);
dispsquare(move[i], EMPTY);
case m of
PREVMOVE:
begin
i := i - 1;
if i < 1 then i := nmoves
end;
NEXTMOVE:
begin
i := i + 1;
if i > nmoves then i := 1
end
end
until m = ACCEPT;
gethuman := move[i]
end
end; { gethuman }
{ GETMOVE.PAS }
function getmove(var list: movelist; pl: pltype): squarenum;
begin { getmove }
Textcolor(lmagenta);
if currentplayer = LIGHT then
begin
GotoXY(31,6);
write('White:')
end
else
begin
GotoXY(31,7);
write('Black:')
end;
Textcolor(lcyan);
if pl = COMPUTERPLAYER then
getmove := getcomputer(list)
else
getmove := gethuman(list);
if currentplayer = LIGHT then
begin
GotoXY(31,6);
write('White:')
end
else
begin
GotoXY(31,7);
write('Black:')
end
end; { getmove }
{ MAKEMOVE.PAS }
procedure makemove(k: squarenum; pl: plcolor);
var
dir: direction;
k1: squarenum;
opponent: plcolor;
del: integer;
begin { makemove }
setsquare(k, pl);
opponent := 1 - pl;
with mainboard do
begin
ndiscs[pl] := ndiscs[pl] + 1;
delmove(k, possible);
for dir := NORTH to NORTHWEST do
begin
del := delta[dir];
if flanking(k, dir, mainboard, pl) then
begin
k1 := k + del;
repeat
setsquare(k1, pl);
ndiscs[pl] := ndiscs[pl] + 1;
ndiscs[opponent] := ndiscs[opponent] - 1;
k1 := k1 + del
until sq[k1] = pl
end
else if sq[k + del] = EMPTY then
addmove(k + del, possible)
end
end
end; { makemove }
{ DECLWINN.PAS }
procedure declarewinner;
var
diff: integer;
s: string[255];
begin { declarewinner }
with mainboard do
diff := ndiscs[LIGHT] - ndiscs[DARK];
if diff > 0 then
begin
itos(diff, 0, s);
GotoXY(25,9);
write('White won by ' + s)
end
else if diff < 0 then
begin
itos(-diff, 0, s);
GotoXY(25,9);
write('Black won by ' + s)
end
else begin
GotoXY(27,9);
write('Game is tied!')
end
end; { declarewinner }
begin { playgame }
initgame;
currentplayer := DARK;
gameover := FALSE;
moved := TRUE;
repeat
dispscore;
if makelist(list, currentplayer, mainboard) > 0 then
begin
moved := TRUE;
k := getmove(list, playertype[currentplayer]);
makemove(k, currentplayer)
end
else if moved then
moved := FALSE
else
gameover := TRUE;
currentplayer := 1-currentplayer
until gameover;
declarewinner
end; { playgame }
begin { main program }
initgraph;
disptitle('R E V E R S I');
center('Version 1.4.1', 2);
initrev;
dispgrid;
buildsquare;
repeat
playgame;
eraseline(24);
GotoXY(1,24);
write('Play again? (Y/N): ');
ch := getkey(ch, ['Y', 'N'], TRUE);
eraseline(24)
until ch = 'N';
Textmode;
ClrScr
end { reversi } .